﻿using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;

namespace PublicDomain.Data.Csv
{
    /// <summary>
    /// Defines interface for the error handler for errors which happen on reading a data row from the source CsvDataReader to the destination DataTable.
    /// </summary>
    /// <param name="exception">The exception which has been intercepted on reading/adding a data row.</param>
    /// <param name="dataRowIndex">The index of the CSV data row.</param>
    /// <returns>Returns true if handled exception should be re-thrown and false if it shouldn't.</returns>
    public delegate bool SqlBulkCopyAdapterReadDataRowErrorHandler(Exception exception, int csvDataRowIndex);

    /// <summary>
    /// Defines interface for the on read data row handler on reading a data row from the source CsvDataReader to the destination DataTable.
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="csvDataRowIndex">Index of the CSV data row.</param>
    public delegate void SqlBulkCopyAdapterReadDataRowHandler(DataRow dataRow, int csvDataRowIndex);

    public static class SqlBulkCopyAdapter
    {
        public static void WriteToServer(this SqlBulkCopy sqlBulkCopy, CsvDataReader dataReader, int chunkSize, SqlBulkCopyAdapterReadDataRowHandler readDataRowHandler, SqlBulkCopyAdapterReadDataRowErrorHandler readDataRowErrorHandler)
        {
            if (sqlBulkCopy == null)
            {
                throw new ArgumentNullException("sqlBulkCopy");
            }

            if (dataReader == null)
            {
                throw new ArgumentNullException("dataReader");
            }

            DataTableMapping dataTableMapping;

            using (var dataTable = CsvDataReaderAdapter.CreateDataTable(sqlBulkCopy.DestinationTableName, dataReader.CsvDataFormat.CsvColumnsDefinitions, out dataTableMapping))
            {
                var chunkCount = 1;
                while (dataReader.ReadToDataTable(
                    dataTable,
                    dataTableMapping,
                    chunkSize,
                    (DataRow dataRow, int csvDataRowIndex) => 
                    { 
                        if (readDataRowHandler != null) 
                        {
                            readDataRowHandler(dataRow, csvDataRowIndex); 
                        } 
                    },
                    (Exception exception, int csvDataRowIndex) => 
                    { 
                        if (readDataRowErrorHandler == null) 
                        { 
                            return true; 
                        }

                        return readDataRowErrorHandler(exception, csvDataRowIndex); 
                    }))
                {
                    chunkCount++;

                    sqlBulkCopy.WriteToServer(dataTable);
                }
            }
        }
    }
}
